use std::io::Write;

enum OpsErr
{
    InputLenError(String),
    FirstOperatorInvalid(String),
    FirstOperatorParseErr(String),
    SeondOperatorInvalid(String),
    SecondOperatorParseErr(String),
    OperatorParseErr(String),
    UnknownOperator(String),
}

impl OpsErr
{
    fn msg(&self)->&str
    {
        match self
        {
            OpsErr::InputLenError(msg)=>
            {
                return msg;
            }
            OpsErr::FirstOperatorInvalid(msg)=>
            {
                return msg;
            }
            OpsErr::FirstOperatorParseErr(msg)=>
            {
                return msg;
            }
            OpsErr::SeondOperatorInvalid(msg)=>
            {
                return msg;
            }
            OpsErr::SecondOperatorParseErr(msg)=>
            {
                return msg;
            }
            OpsErr::OperatorParseErr(msg)=>
            {
                return msg;
            }
            OpsErr::UnknownOperator(msg)=>
            {
                return msg;
            }
        }
    }
}

impl std::fmt::Display for OpsErr
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result 
    {
        return write!(f,"{}",self.msg());
    }
}


trait CalcOps
{
    fn calc(&self)->Result<i32,OpsErr>;
}


struct Operands
{
    x:i32,
    y:i32
}

struct Add(Operands);

impl CalcOps for Add
{
    fn calc(&self)->Result<i32,OpsErr>
    {
        return Ok(self.0.x + self.0.y);
    }
}

struct Minus(Operands);

impl CalcOps for Minus
{
    fn calc(&self)->Result<i32,OpsErr>
    {
        return Ok(self.0.x - self.0.y);
    }
}

struct Devide(Operands);

impl CalcOps for Devide 
{
    fn calc(&self)->Result<i32,OpsErr>
    {
        if 0 == self.0.y
        {
            return Err(OpsErr::SeondOperatorInvalid(String::from("devide operation,but second operator is 0.")));
        }
        return Ok(self.0.x / self.0.y);
    }
}

struct Multiply(Operands);

impl CalcOps for Multiply 
{
    fn calc(&self)->Result<i32,OpsErr>
    {
        return Ok(self.0.x * self.0.y);
    }
}


fn get_ops_obj(oprands:Operands,operator:char)->Option<Box<dyn CalcOps>>
{
    match operator
    {
        '+' =>
        {
            return Some(Box::new(Add(oprands)));
        }
        '-' =>
        {
            return Some(Box::new(Minus(oprands)));
        }
        '*' =>
        {
            return Some(Box::new(Multiply(oprands)));
        }
        '/' =>
        {
            return Some(Box::new(Devide(oprands)));
        }
        _=>
        {
            return Option::None;
        }
    }    
}


fn get_ops(input:&str)->Result<Box<dyn CalcOps>,OpsErr>
{
    let parts:Vec<&str> = input.trim().split_ascii_whitespace().collect();
    if parts.len() != 3
    {
        return Err(OpsErr::InputLenError(String::from("input text len is error.")));
    }

    let op1_res:Result<i32,std::num::ParseIntError> = parts[0].parse(); 
    let op1:i32;
    if let Ok(v) = op1_res
    {
        op1 = v;
    }
    else 
    {
        let error = format!("first operator parse to i32 failed.");
        return Err(OpsErr::FirstOperatorParseErr(error));
    }

    let op2_res:Result<i32,std::num::ParseIntError> = parts[2].parse();
    let op2:i32;
    if let Ok(v) = op2_res
    {
        op2 = v;
    }
    else 
    {
        let error = format!("second operator parse to i32 failed.");
        return Err(OpsErr::FirstOperatorParseErr(error));
    }

    let op_res:Result<char,std::char::ParseCharError> = parts[1].parse();
    let op:char;
    if let Ok(v) = op_res 
    {
        op = v;
    }
    else 
    {
        let error = format!("operator parse failed.");
        return Err(OpsErr::OperatorParseErr(error));
    }

    let obj_res = get_ops_obj(Operands{x:op1,y:op2}, op);
    if let Some(obj) = obj_res
    {
        return Ok(obj);
    }
    else
    {
        let error = format!("unknown operator :{}",op);
        return Err(OpsErr::UnknownOperator(error));
    }

}

fn main()
{
    println!("简易计算器,输入exit退出!");
    println!("支持简单运算：+、-、*、/");

    loop
    {
        print!("> ");
        if let Err(e) = std::io::stdout().flush()
        {
            println!("flush err : {}",e);
            return;
        }

        let mut input = String::new();
        let res = std::io::stdin().read_line(&mut input);
        if let Err(e) = res
        {
            println!("read_line err, {}",e);
            return;
        }


        let input = input.trim();

        if input.eq_ignore_ascii_case("exit")
        {
            println!("Goodbye!");
            return;
        }

        let res_get_ops =  get_ops(&input);

        let calc_ops;
        match res_get_ops
        {
            Ok(v)=>
            {
                calc_ops = v;
            }
            Err(e)=>
            {
                println!("{}",e);
                continue;
            }
        }

        let calc_res = calc_ops.calc();
        match calc_res
        {
            Ok(v) =>
            {
                println!("{} = {}",input,v);
            }
            Err(e) =>
            {
                println!("{}",e);
            }
        }
    }
}




